//=============================================================================
//     _   _                               ____                _  _          
//    | | | |  __ _  _ __   _ __   _   _  / ___|  _ __   _ __ (_)| |_   ___ 
//    | |_| | / _` || '_ \ | '_ \ | | | | \___ \ | '_ \ | '__|| || __| / _ \
//    |  _  || (_| || |_) || |_) || |_| |  ___) || |_) || |   | || |_ |  __/
//    |_| |_| \__,_|| .__/ | .__/  \__, | |____/ | .__/ |_|   |_| \__| \___|
//                  |_|    |_|     |___/         |_|                         
//
//                     HappySprite - We make sprites happy
//
// Copyright (c) 2007 by Tank Monkey Games
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace HappySprite
{
    public class Transform2D
    {
        Vector2 position;
        Vector2 scale = Vector2.One;
        float rotation;

        float cos = 1f; 
        float m11 = 1f;
        float m12 = 0f;
        float m21 = 0f;
        float m22 = 1f;

        bool matrixIsCurrent = true;

        public Transform2D()
        {
        }

        public float M11
        {
            get { if (!matrixIsCurrent) ComputeMatrix(); return m11; }
        }

        public float M12
        {
            get { if (!matrixIsCurrent) ComputeMatrix(); return m12; }
        }

        public float M21
        {
            get { if (!matrixIsCurrent) ComputeMatrix(); return m21; }
        }

        public float M22
        {
            get { if (!matrixIsCurrent) ComputeMatrix(); return m22; }
        }

        public Vector2 Position
        {
            get { return position; }
            set { position = value; OnTransformChanged(); }
        }

        public float PositionX
        {
            get { return position.X; }
            set { position.X = value; OnTransformChanged(); }
        }

        public float PositionY
        {
            get { return position.Y; }
            set { position.Y = value; OnTransformChanged(); }
        }

        public float Rotation
        {
            get { return rotation; }
            set { rotation = value; matrixIsCurrent = false; OnTransformChanged(); }
        }

        public Vector2 Scale
        {
            get { return scale; }
            set { scale = value; matrixIsCurrent = false; OnTransformChanged(); }
        }

        public float ScaleX
        {
            get { return scale.X; }
            set { scale.X = value; matrixIsCurrent = false; OnTransformChanged(); }
        }

        public float ScaleY
        {
            get { return scale.Y; }
            set { scale.Y = value; matrixIsCurrent = false; OnTransformChanged(); }
        }

        public void TransformPoint(ref Vector2 point, out Vector2 result)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            result.X = point.X * m11 + point.Y * m21 + position.X;
            result.Y = point.X * m12 + point.Y * m22 + position.Y;
        }

        public Vector2 TransformPoint(Vector2 point)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            Vector2 result;
            result.X = point.X * m11 + point.Y * m21 + position.X;
            result.Y = point.X * m12 + point.Y * m22 + position.Y;
            return result;
        }

        public void TransformVector(ref Vector2 vector, out Vector2 result)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            result.X = vector.X * m11 + vector.Y * m21;
            result.Y = vector.X * m12 + vector.Y * m22;
        }

        public Vector2 TransformVector(Vector2 vector)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            Vector2 result;
            result.X = vector.X * m11 + vector.Y * m21;
            result.Y = vector.X * m12 + vector.Y * m22;
            return result;
        }

        public void TransformTranspose(ref Vector2 value, out Vector2 result)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            result.X = value.X * m11 + value.Y * m12;
            result.Y = value.X * m21 + value.Y * m22;
        }

        public Vector2 TransformTranspose(Vector2 value)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            Vector2 result;
            result.X = value.X * m11 + value.Y * m12;
            result.Y = value.X * m21 + value.Y * m22;
            return result;
        }

        public Vector2 TransformVectorWithoutScale(Vector2 vector)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            Vector2 result;
            result.X = vector.X * cos + vector.Y * m21;
            result.Y = vector.X * m12 + vector.Y * cos;
            return result;
        }

        public Vector2 TransformTransposeWithoutScale(Vector2 value)
        {
            if (!matrixIsCurrent) ComputeMatrix();
            Vector2 result;
            result.X = value.X * cos + value.Y * m12;
            result.Y = value.X * m21 + value.Y * cos;
            return result;
        }

        protected virtual void OnTransformChanged()
        {
        }

        void ComputeMatrix()
        {
            float sin = Trigonometry.Sin(rotation);
            cos = Trigonometry.Cos(rotation);
            m11 = scale.X * cos;
            m12 = sin;
            m21 = -sin;
            m22 = scale.Y * cos;
            matrixIsCurrent = true;
        }
    }
}
